package miniprojet;

/**
 * 
 * Le serveur gère une structure de données permettant de stocker des moyens de transport
 * ayant une certaine capacité de voyageurs, effectuant certains trajets à certaines dates
 * et selon certains horaires et de les retrouver.
 * 
 * Le serveur est responsable de la persistance des données.
 * Le serveur doit pouvoir retrouver un moyen de transport 
 * ayant une ou des places disponibles pour un certain trajet à une certaine date. 
 * 
 * @author Alexis Laurent & Antoine Lavail
 * @version 2011.11.21
 */

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import miniprojet.Transport.EnumTransport;

public class Serveur {

    // Chaine de caractères définissant le nom du fichier de sérialisation
    // d'objet.
    public static String CHEMIN_DATA = "Serv-Data.bin";

    // Chaine de caractères définissant le nom du fichier de sérialisation en
    // texte.
    public static String CHEMIN_TEXT = "Serv-Text.txt";

    // Liste des transports
    private List<Transport> transports;

    /**
     * Constructeur de la classe serveur.
     * 
     * Ce constructeur est paramétré pour utiliser la sérialisation d'objet. Il
     * utilise donc comme nom de fichier le CHEMIN_DATA et utilise
     * serializeData().
     */
    public Serveur() {
        File f = new File(CHEMIN_DATA);
        if (f.exists()) {
            transports = new ArrayList<Transport>();
            deserializeData();
        } else
            transports = new ArrayList<Transport>();
    }

    /**
     * Accesseur pour l'attribut liste transport
     * 
     * @return la liste de transport.
     */
    public List<Transport> getTransports() {
        return transports;
    }

    /**
     * Cette méthode permet la sérialisation de données d'un objet. Elle
     * sérialise l'attribut de la classe Serveur, l'attribut transports. La
     * méthode enregistre dans un fichier .bin.
     */
    private void serializeData() {
        try {
            // ouverture d'un flux de sortie vers le fichier "Serv-Data.serial"
            FileOutputStream fos = new FileOutputStream(CHEMIN_DATA);

            // création d'un "flux objet" avec le flux fichier
            ObjectOutputStream oos = new ObjectOutputStream(fos);
            try {
                // sérialisation : écriture de l'objet dans le flux de sortie
                oos.writeObject(transports);
                // on vide le tampon
                oos.flush();
            } finally {
                // fermeture des flux
                try {
                    oos.close();
                } finally {
                    fos.close();
                }
            }
        } catch (IOException ioe) {
            ioe.printStackTrace();
        }
    }

    /**
     * Déserialise un fichier de données contenant un objet. Ici, on transfère
     * l'objet contenu dans le fichier de donnée dans l'attribut transports.
     */
    private void deserializeData() {

        try {
            // ouverture d'un flux d'entrée depuis le fichier "Serv-Data.serial"
            FileInputStream fis = new FileInputStream(CHEMIN_DATA);

            // création d'un "flux objet" avec le flux fichier
            ObjectInputStream ois = new ObjectInputStream(fis);
            try {
                // désérialisation : lecture de l'objet depuis le flux d'entrée
                transports = (ArrayList<Transport>) ois.readObject();
            } finally {
                // on ferme les flux
                try {
                    ois.close();
                } finally {
                    fis.close();
                }
            }
        } catch (IOException ioe) {
            ioe.printStackTrace();
        } catch (ClassNotFoundException cnfe) {
            cnfe.printStackTrace();
        }
    }

    /**
     * Sauvegarde dans un fichier texte l'ensemble des transports présents dans
     * le serveur.
     */
    private void serializeText() {

        try {

            // Instancier un FileWriter, en paramètre le nom du fichier et true
            // signifie qu'on ajoute dans le fichier, on ecrase pas les données
            FileWriter fw = new FileWriter(CHEMIN_TEXT, true);

            // Instancier le BufferedWriter output auquel on donne comme
            // argument le FileWriter fw cree juste au dessus
            BufferedWriter output = new BufferedWriter(fw);

            Iterator<Transport> it = transports.iterator();

            while (it.hasNext()) {
                Transport t = it.next();

                List<Trajet> lt = t.getTrajets();
                Iterator<Trajet> itl = lt.iterator();

                // Ecrire dans le BufferedWriter le texte récupéré en paramètre
                output.write(t.getTransport() + "\n");
                output.write(t.getCapacity() + "\n");

                while (itl.hasNext()) {
                    Trajet tr = itl.next();

                    List<Voyageur> lv = tr.getVoyageurs();
                    Iterator<Voyageur> itv = lv.iterator();

                    output.write(tr.getDateDep() + "\n" + tr.getDateArr()
                            + "\n" + tr.getHeureDep() + "\n" + tr.getHeureArr()
                            + "\n" + tr.getDepart() + "\n" + tr.getArrive()
                            + "\n");

                    while (itv.hasNext()) {
                        Voyageur v = itv.next();
                        output.write(v.getNom() + "\n" + v.getPrenom() + "\n");
                    }
                    output.write("endVoyageurs\n");
                }
                output.write("endTrajets\n");
            }
            output.write("endTransports\n");

            // Envoyer dans le fichier le BufferedWriter
            output.flush();

            // Fermer le BufferedWriter
            output.close();

        } catch (IOException ioe) {
            ioe.printStackTrace();
        }

    }

    /**
     * Déserialise un fichier de données contenant du texte. Ici, on transfère
     * les attributs d'un transport contenu dans le fichier de donnée dans
     * l'attribut transports.
     */
    private void deserializeText() {
        try {

            String text = new String();
            transports = new ArrayList<Transport>();

            // Donner le nom du fichier
            String nomFichier = CHEMIN_TEXT;

            // Instancier un FileReader pour lire le fichier souhaiter
            FileReader fr = new FileReader(nomFichier);

            // Instancier un BufferReader
            BufferedReader br = new BufferedReader(fr);

            // Construction des objets

            while (!(text = br.readLine()).equals("endTransports")) {
                Transport e = new Transport(EnumTransport.valueOf(text
                        .toUpperCase()), Integer.valueOf(br.readLine()));

                List<Trajet> lt = new ArrayList<Trajet>();
                while (!(text = br.readLine()).equals("endTrajets")) {
                    String dateD = text;

                    String dateA = br.readLine();
                    String heureDep = br.readLine();
                    String heureArr = br.readLine();
                    String depart = br.readLine();
                    String arrive = br.readLine();
                    Trajet t = new Trajet(dateD, dateA, heureDep, heureArr,
                            depart, arrive);

                    lt.add(t);
                    List<Voyageur> lv = new ArrayList<Voyageur>();

                    while (!(text = br.readLine()).equals("endVoyageurs")) {
                        String nom = text;
                        String prenom = br.readLine();
                        Voyageur v = new Voyageur(nom, prenom);
                        lv.add(v);
                    }
                    t.setVoyageurs(lv);
                }
                e.setTrajets(lt);
                transports.add(e);
            }

            // Fermer le BufferedReader
            //
            br.close();
        } catch (IOException ioe) {
            ioe.printStackTrace();
        }
    }

    /**
     * Méthode permettant de rechercher une liste de transport à partir de deux
     * critères : Une ville de départ, une ville d'arrivée et une date.
     * 
     * @param dep
     *            : La ville de départ.
     * @param arr
     *            : La ville d'arrivée.
     * @param d
     *            : La date du voyage.
     * @return : La liste de transport correspondant à la recherche.
     */
    public List<Transport> recherche(String dep, String arr, String d) {
        List<Transport> l = new ArrayList<Transport>();
        Iterator<Transport> it = transports.iterator();
        while (it.hasNext()) {
            Transport t = it.next();
            Iterator<Trajet> it2 = t.getTrajets().iterator();
            while (it2.hasNext()) {
                Trajet t2 = it2.next();
                if (t2.getDepart().equals(dep) && t2.getArrive().equals(arr)
                        && t2.getDateDep().equals(d)
                        && t2.getVoyageurs().size() < t.getCapacity())
                    l.add(t);
            }
        }
        return l;
    }

    /**
     * Méthode permettant d'enregistrer un voyageur dans un trajet qui
     * correspond lui-même à un transport.
     * 
     * @param t
     *            : Une référence sur le transport où le voyageur doit être
     *            ajouté.
     * @param tr
     *            : Une référence sur le trajet où le voyageur doit être ajouté.
     * @param v
     *            : Le voyageur a ajouter au trajet donné.
     */

    public void enregistrerVoyageur(Transport t, Trajet tr, Voyageur v) {
        Iterator<Transport> it = transports.iterator();
        while (it.hasNext()) {
            Transport t2 = it.next();
            if (t2.equals(t)) {
                Iterator<Trajet> it2 = t2.getTrajets().iterator();
                while (it.hasNext()) {
                    Trajet t3 = it2.next();
                    if (t3.equals(tr)) {
                        t3.addVoyageur(v);
                        return;
                    }
                }
            }
        }
    }

    /**
     * Méthode permettant de supprimer un voyageur dans un trajet qui correspond
     * lui-même à un transport.
     * 
     * @param t
     *            : Une référence sur le transport où le voyageur doit être
     *            ajouté.
     * @param tr
     *            : Une référence sur le trajet où le voyageur doit être ajouté.
     * @param v
     *            : Le voyageur a supprimer au trajet donné.
     */

    public void supprimerVoyageur(Transport t, Trajet tr, Voyageur v) {
        Iterator<Transport> it = transports.iterator();
        while (it.hasNext()) {
            Transport t2 = it.next();
            if (t2.equals(t)) {
                Iterator<Trajet> it2 = t2.getTrajets().iterator();
                while (it.hasNext()) {
                    Trajet t3 = it2.next();
                    if (t3.equals(tr)) {
                        t3.removeVoyageur(v);
                        return;
                    }
                }
            }
        }
    }

    /**
     * Permet un accès à la sérialisation du serveur.
     */
    public void sauvegarder() {
        serializeData();
    }
}
